#include "process.h"
#include "schedule.h"
#include "queue.h"
#include "observer.h"
#include "simulator.h"

#include <math.h>
#include <stdlib.h>
#include <stdio.h>


/* in ms*/
#define EXP_TS_BASE		10

#define EXP_PRIORITIES		8

/* an exponential queue*/
struct qQueue *expQueues[EXP_PRIORITIES];
int expNextEventTime;

void exp_init(void);
struct process *exp_schedule(void);
int exp_time_to_event(void);
void exp_add(struct process *proc);
int exp_done();
void exp_die(char *);
/* By Jacob Eichers and Cliff Buchanan, contains the functions for the 
	exponential scheduler*/

/* schedFuncTable with links to exp calls*/
struct schedFuncTable expSchedTab =
{
	&exp_init,
	&exp_schedule,
	&exp_time_to_event,
	&exp_add,
	&exp_done
};


/* Initalization for exp scheduler. Must be performed before any other
	calls are made to this scheduler.*/
void exp_init()
{
	int i;
	
	for(i=0; i<EXP_PRIORITIES; i++)
	{
		expQueues[i] = q_create();
		if(!expQueues[i])
			{exp_die("exp_init");}
	}
	expNextEventTime = 0;
}

/* Returns the next job as decided by the scheduler. Requires done()==0.*/
struct process *exp_schedule()
{
	int i;
	struct process *p;

	p = NULL;
	for(i=0; !p && i<EXP_PRIORITIES; i++)
	{
		if(!q_is_empty(expQueues[i]))
			{p = q_remove(expQueues[i]);}
	}
	
	if(p)
	{
		expNextEventTime = sim_get_clock()+
			(int)exp2l(p->priority)*EXP_TS_BASE;

		return p;
	}
	else
	{
		fprintf(stderr, "exp_schedule: Scheduler empty!\n");
		exp_die("exp_schedule");
		return NULL;
	}
}

/* When done()!=0, time until the scheduler wants to interrupt the machine and
	perform an active rescheduling otherwise, undefined.*/
int exp_time_to_event()
{
	int timeLeft = expNextEventTime-sim_get_clock();

	return timeLeft>0 ? timeLeft : 0;
}

/* Add a process to the scheduler*/
void exp_add (struct process *proc)
{
	int ts;
	
	/* re-evaluate process priority*/
	ts = (int)exp2l(proc->priority)*EXP_TS_BASE;
	/* if it ran less than half the time slice, promote it*/
	if(ts/2.>proc->lastRun && proc->priority-1>=0)
		{proc_set_priority(proc, proc->priority-1);}
	/* if it ran more than half the time slice, demote it*/
	else if(ts<=(proc->lastRun+SIM_CONTEXT_SWITCH_TIME) && proc->priority+1<EXP_PRIORITIES)
		{proc_set_priority(proc, proc->priority + 1);}
	q_add(expQueues[proc->priority], proc);
}

/* Returns 0 iff the scheduler has jobs left.*/
int exp_done()
{
	int i;

	for(i=0; i<EXP_PRIORITIES; i++)
	{
		if(!q_is_empty(expQueues[i]))
			{return 0;}
	}
	
	return 1;
}

void exp_die(char *caller)
{
	fprintf(stderr, "%s: critical error! Exiting...\n", caller);
	exit(-1);
}
